package Index;

import gitobject.Blob;
import gitobject.GitObject;
import repository.Repository;
import zlib.ZLibUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @author shkstart
 * @create 2021-12-01 19:25
 */
public class Index implements Serializable {
    private static final Long serialVersionUID = 3455710433843546890l;
    private TreeNode indexTree;           //存放当前暂存区的目录树
    private ArrayList<String> blobList;     //存放暂存区中现有文件及其位置
    private String INDEXPATH = new File(".").getCanonicalPath() + File.separator + ".jit" + File.separator + "index";
    public Index() throws IOException {
        indexTree = new TreeNode();
        blobList = new ArrayList<>();
        if(readObject() != null){
            indexTree = readObject().indexTree;
            blobList = readObject().getBlobList();
        }
    }

    public Index(TreeNode indexTree, ArrayList<String> blobList, String INDEXPATH) throws IOException {
        this.indexTree = indexTree;
        this.blobList = blobList;
        this.INDEXPATH = INDEXPATH;
    }

    public TreeNode getIndexTree() {
        return indexTree;
    }

    public void setIndexTree(TreeNode indexTree) {
        this.indexTree = indexTree;
    }

    public ArrayList<String> getBlobList() {
        return blobList;
    }

    public void setBlobList(ArrayList<String> blobList) {
        this.blobList = blobList;
    }

    public String getINDEXPATH() {
        return INDEXPATH;
    }

    public void setINDEXPATH(String INDEXPATH) {
        this.INDEXPATH = INDEXPATH;
    }


    public TreeNode updateIndex(TreeNode treeNode, TreeNode indexTemp) throws IOException {    //采用层次遍历法更新每次提交的目录树
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.add(indexTemp);
        while (!q.isEmpty()){
            int len = q.size();
            for (int i = 0; i < len; i++) {
                TreeNode temp = q.poll();
                if(temp.getpath().equals(treeNode.getpath()) && temp.getId().equals( treeNode.getId())){ //路径相同且hash值相同，直接返回
                    return indexTemp;
                }
                if(temp.getpath().equals(treeNode.getpath()) && !temp.getId().equals( treeNode.getId())){ //路径相同但hash值不同，用新结点代替原来的结点
                    temp = treeNode;
                    return indexTemp;
                }
                if(temp.getChildren() != null){
                    for (int j = 0; j < temp.getChildren().size(); j++) {
                        q.add(temp.getChildren().get(j));
                    }
                }
            }
        }
        //若遍历完索引树均为找到路径相同的结点，则合并新结点与原来的头结点，创建新的头结点
        String tempId = indexTemp.toString() + treeNode.toString();
        String tree = "";
        //求新头结点hash值
        try {
            MessageDigest messageDigest = null;
            messageDigest = MessageDigest.getInstance("SHA-1");
            messageDigest.update(tempId.getBytes(StandardCharsets.UTF_8));
            byte[] hashCode = messageDigest.digest();
            for (int i = 0; i < hashCode.length; i++) {
                if ((int)(hashCode[i] & 0xFF) < 16) { // avoiding the lack of '0'
                    tree += '0';
                }
                tree += Integer.toString(hashCode[i] & 0xFF, 16);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        TreeNode temp = new TreeNode();
        temp.addChildren(treeNode);
        temp.addChildren(indexTemp);
        temp.setType("tree");
        temp.setBase("tree");
        temp.setId(tree);
        temp.setpath(new File(".").getCanonicalPath() + File.separator + "index");
        return temp;
    }

    /**
     * Todo: Serialize the object to the indexFile
     * @throws Exception
     */
    public void writeObject(){

        ObjectOutputStream oos = null;
        try{
            oos = new ObjectOutputStream(new FileOutputStream(INDEXPATH));
            oos.writeObject(this);
            oos.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if(oos != null){
                try {
                    oos.close();;
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Todo: Serialize the object and compress with zlib.
     * @throws Exception
     */
    public void compressWrite(String temPath){
        /**
         * Add your code here.
         */
        ByteArrayOutputStream os = null;
        ObjectOutputStream oos = null;
        FileOutputStream f = null;
        try{
            /**
             * Todo: Add your code here.
             */
            os = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(os);
            f = new FileOutputStream(temPath);
            oos.writeObject(this);
            byte[] inputData = os.toByteArray();
            byte[] data = ZLibUtils.compress(inputData);
            ZLibUtils.compress(data, f);
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if(oos != null){
                try {
                    oos.close();;
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

    public Index readObject(){

        ObjectInputStream ois = null;
        Index index = null;
        try{
            File file = new File(INDEXPATH);
            if (file.length() == 0){

                return null;
            }

            ois = new ObjectInputStream(new FileInputStream(file));
            index = (Index) ois.readObject();
            return index;
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if(ois != null){
                try {
                    ois.close();;
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}
